#!python
""" pbfi.py
Tool to inspect binary files
"""
__version__ = "0.1 alpha"

import pbfi_model_parser

import sys
import struct
import util

def reader_to_pack_format(reader):
    format = ""
    rformat = reader['format']
    rsize = ""
    if 'size' in reader: 
        rsize = str(reader['size'])
    
    if rformat == 'int':
        format = ">" + rsize + "i"
    elif rformat == 'int16':
        format = ">" + rsize + "h"
    elif rformat == 'int64':
        format = ">" + rsize + "q"
    elif rformat == 'string':
        format = ">" + rsize + "s"
    elif rformat == 'byte':
        format = ">" + rsize + "b"
    elif rformat == 'float':
        format = ">" + rsize + "f"
    elif rformat == 'float64':
        format = ">" + rsize + "d"
    return format, struct.calcsize(format)


def read_blocks_from_sections(binary_file, sections, context):
    """
    Traverse a list of sections and create the list of blocks
    """
    blocks = []
    for section in sections:
        blocks.append(read_block_from_section(binary_file, section, context))
    return blocks

def read_block_from_section(binary_file, section, context):
    """
    Create a block from a section according to its type
    """
    (reader, display) = section
    if reader['format'] == 'array':
        reading_array = []
        count = context[reader['count']]
        for i in xrange(count):
            blocks = read_blocks_from_sections(binary_file, reader['entries'], context)
            reading_array.extend(blocks)
        block = {	
                'format':'array', 
                'count':count,
                'name':reader['name'],
                'display':display, 
                'value':reading_array, 
                }
        return block
    else:
        format, size = reader_to_pack_format(reader)
        bytes = binary_file.read(size)
        value = struct.unpack(format, bytes)
        if reader['id']: 
            context[reader['id']] = value[0]
        block = {
                'format':format,
                'name':reader['name'],
                'display':display, 
                'value':value[0], 
                }
        return block

class BinaryFileInspector(util.Observer,util.Subject):
    def __init__(self):
        util.Subject.__init__(self)
        self.blocks = None
        self.sections = None
        self.binary_filename = None
        self.model_filename = None
    
    def load_model(self,model_filename):
        self.model_filename = model_filename
        self.reload_model()
    
    def reload_model(self):
        self.sections = pbfi_model_parser.parse(self.model_filename)
        self.notify()
    
    def load(self,binary_filename):
        self.binary_filename = binary_filename
        self.reload()
    
    def reload(self):
        fileobj = open(self.binary_filename, mode='rb') 
        self.blocks = read_blocks_from_sections(fileobj, self.sections, dict())
        fileobj.close()
        self.notify()

    def update(self):
        self.notify()
    

def display_blocks(blocks):
    """
    Simple display function
    """
    for block in blocks:
        if block['format'] == 'array':
            display_blocks(block['value'])
        else:
            print block['value']

def main(argv=None):
    """
    Inspect a binary file (second argument) according to the model file (first argument)
    then do a dummy display of the result
    """
    bfi = BinaryFileInspector()
    bfi.load_model(sys.argv[1])
    bfi.load(sys.argv[2])
    blocks = bfi.blocks 
    display_blocks(blocks)

if __name__ == "__main__":
    main()

